import React from 'react'
import style from './autoComplete.scss'
import { SelectedableItem } from './selectableItem'
import { mergeStyle } from './mergeStyle'

export const AutoComplete = ({ placeholder, inputClassName, renderItem, dataSource, value,
    onChangeText, onSelectItem, getValue, readOnly, onClickQuickEdit }) => {
    const [showDropdown, setShowDropdown] = React.useState(false)
    const [previewSelectIndex, setPreviewSelectIndex] = React.useState(dataSource.findIndex(item => getValue(item) === value))
    const onKeyDownHandler = React.useCallback(ev => {
        const pageSize = 10
        if (showDropdown) {
            const { keyCode } = ev
            switch (keyCode) {
                case 27:    //esc
                    setShowDropdown(false)
                    ev.preventDefault()
                    break
                case 33:    //pageUp
                    setPreviewSelectIndex(index => Math.max(0, index - pageSize))
                    ev.preventDefault()
                    break
                case 34:    //pageDown
                    setPreviewSelectIndex(index => Math.min(dataSource.length - 1, index + pageSize))
                    ev.preventDefault()
                    break
                case 38:    //up
                    setPreviewSelectIndex(index => Math.max(0, index - 1))
                    ev.preventDefault()
                    break
                case 40:    //down
                    setPreviewSelectIndex(index => Math.min(dataSource.length - 1, index + 1))
                    ev.preventDefault()
                    break
                case 13:    //enter
                    setPreviewSelectIndex(-1)
                    setShowDropdown(false)
                    onSelectItem(dataSource[previewSelectIndex])
                    ev.preventDefault()
                    break
                default:
                    break
            }
        }
    }, [previewSelectIndex, dataSource, showDropdown, onSelectItem])
    const inputRef = React.useRef()
    const dropdownRef = React.useRef()
    const onFocusHandler = React.useCallback(ev => {
        if (!readOnly) {
            ev.target.select()
            setShowDropdown(true)
        }
    }, [readOnly])
    const hideDropdownHandler = React.useCallback(ev => {
        const target = ev.target
        if (inputRef && target === inputRef.current) {
            return
        }

        const dropdownEl = dropdownRef ? dropdownRef.current : null
        if (!dropdownEl) {
            return
        }

        const isChildOfDropdown = target && dropdownEl && dropdownEl.contains(target)
        if (!isChildOfDropdown) {
            setShowDropdown(false)
            setPreviewSelectIndex(-1)

            const matchedItem = dataSource.find(item => getValue(item) === inputRef.current.value)
            if (matchedItem && getValue(matchedItem) !== value) {
                onSelectItem(matchedItem)
            }
        }
    }, [dataSource, onSelectItem])

    React.useEffect(() => {
        document.body.addEventListener('click', hideDropdownHandler)
        return () => document.body.removeEventListener('click', hideDropdownHandler)
    }, [])

    React.useEffect(() => {
        if (previewSelectIndex >= 0 && previewSelectIndex < dataSource.length && dropdownRef && dropdownRef.current) {
            const dropdownEl = dropdownRef.current,
                itemElOfPreviewIndex = dropdownEl.children[previewSelectIndex]

            if (itemElOfPreviewIndex) {
                itemElOfPreviewIndex.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' })
            }
        }
    }, [previewSelectIndex])

    const wrapSelectItemAndHideDropdown = React.useCallback((item) => {
        value !== getValue(item) && onSelectItem(item)
        setShowDropdown(false)
        setPreviewSelectIndex(-1)
    }, [value, dataSource, onSelectItem])

    const wrapOnChangeAndShowDropdown = React.useCallback((ev) => {
        onChangeText(ev)
        setPreviewSelectIndex(0)
        setShowDropdown(true)
    }, [onChangeText])

    const wrapOnShowDropDown = React.useCallback(() => {
        !readOnly && setShowDropdown(true)
    }, [readOnly])

    const activeItem = dataSource.find(item => getValue(item) === value)
    const inputValue = activeItem ? renderItem(activeItem) : value

    const wrapOnClickQuickEdit = React.useCallback(() => {
        typeof onClickQuickEdit === 'function' && onClickQuickEdit(activeItem)
    }, [onClickQuickEdit, activeItem])

    return (
        <div className={style['auto_complete']}>
            <input className={mergeStyle(style, 'input', inputClassName)} value={inputValue} title={inputValue} ref={inputRef}
                onChange={wrapOnChangeAndShowDropdown} onKeyDown={onKeyDownHandler} placeholder={placeholder}
                onFocus={onFocusHandler} onClick={wrapOnShowDropDown} readOnly={readOnly} />
            {onClickQuickEdit && activeItem && <a className={style['quick_edit']} onClick={wrapOnClickQuickEdit}>edit</a>}
            {showDropdown && <div className={style['dropdown']} ref={dropdownRef}>
                {dataSource.length
                    ? dataSource
                        .map((item, index) => {
                            const currentValue = getValue(item)

                            return (
                                <SelectedableItem key={getValue(item)} item={item} onSelect={wrapSelectItemAndHideDropdown}
                                    active={value === currentValue} preview={index === previewSelectIndex}>
                                    {renderItem(item)}
                                </SelectedableItem>)
                        }) : <div className={style['no_item']}>[空]</div>}
            </div>}
        </div>
    )
}